/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "MUpdateCheck.h"

#include "VersionNumber.h"

//---------------------------------------------------------------------------

#define MAX_BUFF_SIZE 4096

//---------------------------------------------------------------------------

MUpdateCheck::MUpdateCheck(void)
{
	winSockInitialized = false;
}

MUpdateCheck::~MUpdateCheck(void)
{
	CleanUp();
}

//---------------------------------------------------------------------------

bool MUpdateCheck::Initialize(void)
{
	WORD reqVersion;
	WSADATA data;
	int err;

	if(winSockInitialized)
		CleanUp();

	reqVersion = MAKEWORD(2, 2);
	err = WSAStartup(reqVersion, &data);
	if(err!=0)
		return false;

	if(LOBYTE(reqVersion)!=2 && HIBYTE(reqVersion)!=2){
		CleanUp();
		return false;
	}

	winSockInitialized = true;
	return true;
}

void MUpdateCheck::CleanUp(void)
{
	winSockInitialized = false;
	WSACleanup();
}

//---------------------------------------------------------------------------

bool MUpdateCheck::ResolveAddress(std::string srvAddrIn)
{
	int res;

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family   = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	res = getaddrinfo(srvAddrIn.c_str(), "80", &hints, &addr);
	if(res!=0) return false;

	return true;
}

int MUpdateCheck::GetData(std::string srvAddrIn, std::string fileNameIn)
{
	SOCKET sock;
	bool done;
	int res, idx;
	std::string request;
	unsigned long availableDataSize;
	int retVal;

	if(!ResolveAddress(srvAddrIn))
		return -3;

	sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
	if(sock==INVALID_SOCKET){
		freeaddrinfo(addr);
		return -4;
	}

	res = connect(sock, addr->ai_addr, (int)addr->ai_addrlen);
	if(res==SOCKET_ERROR){
		closesocket(sock);
		freeaddrinfo(addr);
		return -5;
	}

	request = "GET ";
	request += fileNameIn;
	request += " HTTP/1.1\r\n";

	request += "Host: ";
	request += srvAddrIn;
	request += "\r\n";

	request += "Accept: text/html\r\n";

	request += "\r\n";

	res = send(sock, request.c_str(), (int)request.length(), 0);
	if(res!=request.length()){
		closesocket(sock);
		freeaddrinfo(addr);
		return -6;
	}

	// wait for some data to arrive
	idx = 0;
	while(idx<10){
		Sleep(100);
		res = ioctlsocket(sock, FIONREAD, &availableDataSize);
		if(res==SOCKET_ERROR)
			return -7;

		if(availableDataSize>0) idx = 10;
		else idx++;
	}

	if(availableDataSize==0)
		return -8;

	idx = 0;
	done = false;
	retVal = 1;
	while(!done){
		res = ioctlsocket(sock, FIONREAD, &availableDataSize);
		if(res==SOCKET_ERROR){
			done = true;
			retVal = -9;
		}
		else{
			if(availableDataSize>0){
				res = recv(sock, &readBuff[idx], MAX_BUFF_SIZE - idx, 0);
				switch(res){
					case 0:
						// connection closed
						done = true;
						break;

					case SOCKET_ERROR:
						done = true;
						retVal = -10;
						break;

					default:
						idx += res;
						break;
				}

				if(idx>=MAX_BUFF_SIZE){
					done = true;
					retVal = -11;
				}
			}
			else{
				done = true;
			}
		}
	}
	
	closesocket(sock);
	freeaddrinfo(addr);
	return retVal;
}

int MUpdateCheck::CheckData(void)
{
	std::string vStr, cStr;
	std::string::size_type pos1, pos2;

	vStr = readBuff;

	pos1 = vStr.find("<meta version");
	if(pos1==std::string::npos) return -12;
	pos1 += 15;

	pos2 = vStr.find("\"", pos1);
	if(pos2==pos1==std::string::npos) return -13;

	vStr = vStr.substr(pos1, pos2 - pos1);
	cStr = APPLICATION_VERSION;

	xVersionFound = vStr;

	if(vStr.compare(cStr)==0) return 0;

	return 1;
}

int MUpdateCheck::Check(std::string srvAddrIn, std::string fileNameIn)
{
	int retVal;

	xVersionFound.clear();

	if(!winSockInitialized){
		if(!Initialize())
			return -1;
	}

	try{
		readBuff = new char[MAX_BUFF_SIZE + 1];
	}
	catch(...){
		readBuff = NULL;
	}
	if(readBuff==NULL){
		CleanUp();
		return -2;
	}

	ZeroMemory(readBuff, MAX_BUFF_SIZE + 1);

	retVal = GetData(srvAddrIn, fileNameIn);
	if(retVal<0){
		delete[] readBuff;
		CleanUp();
		return retVal;
	}

	retVal = CheckData();

	delete[] readBuff;
	CleanUp();
	return retVal;
}

//---------------------------------------------------------------------------
